package com.chengyi.serialport.demo.gson

import com.google.gson.TypeAdapterFactory
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import com.google.gson.TypeAdapter
import android.os.Bundle
import kotlin.Throws
import android.util.Pair
import androidx.core.os.bundleOf
import com.google.gson.stream.JsonReader
import com.google.gson.stream.JsonToken
import com.google.gson.stream.JsonWriter
import java.io.IOException
import java.util.ArrayList

/*
 * Copyright (C) 2015 Gson Type Adapter Authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */ /**
 * Type adapter for Android Bundle. It only stores the actual properties set in the bundle
 *
 * @author Inderjeet Singh
 */
class BundleTypeAdapterFactory : TypeAdapterFactory {
    override fun <T> create(gson: Gson, type: TypeToken<T>): TypeAdapter<T>? {
        if (!Bundle::class.java.isAssignableFrom(type.rawType)) {
            return null
        }
        return object : TypeAdapter<Bundle?>() {
            @Throws(IOException::class)
            override fun write(out: JsonWriter, bundle: Bundle?) {
                if (bundle == null) {
                    out.nullValue()
                    return
                }
                out.beginObject()
                for (key in bundle.keySet()) {
                    out.name(key)
                    val value = bundle[key]
                    if (value == null) {
                        out.nullValue()
                    } else {
                        gson.toJson(value, value.javaClass, out)
                    }
                }
                out.endObject()
            }

            @Throws(IOException::class)
            override fun read(inReader: JsonReader): Bundle? {
                return when (inReader.peek()) {
                    JsonToken.NULL -> {
                        inReader.nextNull()
                        null
                    }
                    JsonToken.BEGIN_OBJECT -> toBundle(readObject(inReader))
                    else -> throw IOException("expecting object: " + inReader.path)
                }
            }

            @Throws(IOException::class)
            private fun toBundle(values: List<Pair<String, Any?>>): Bundle {
                val pairList = ArrayList<kotlin.Pair<String, Any?>>()
                values.forEach {
                    pairList.add(kotlin.Pair(it.first, it.second))
                }
                return bundleOf(*pairList.toTypedArray())
            }

            @Throws(IOException::class)
            private fun readObject(inReader: JsonReader): List<Pair<String, Any?>> {
                val list: MutableList<Pair<String, Any?>> = ArrayList()
                inReader.beginObject()
                while (inReader.peek() != JsonToken.END_OBJECT) {
                    when (inReader.peek()) {
                        JsonToken.NAME -> {
                            val name = inReader.nextName()
                            val value = readValue(inReader)
                            list.add(Pair(name, value))
                        }
                        JsonToken.END_OBJECT -> {}
                        else -> throw IOException("expecting object: " + inReader.path)
                    }
                }
                inReader.endObject()
                return list
            }

            @Throws(IOException::class)
            private fun readValue(inReader: JsonReader): Any? {
                return when (inReader.peek()) {
                    JsonToken.BEGIN_ARRAY -> readArray(inReader)
                    JsonToken.BEGIN_OBJECT -> {
                        val pairList = ArrayList<kotlin.Pair<String, Any?>>()
                        readObject(inReader).forEach {
                            pairList.add(kotlin.Pair(it.first, it.second))
                        }
                        hashMapOf(*pairList.toTypedArray())
                    }
                    JsonToken.BOOLEAN -> inReader.nextBoolean()
                    JsonToken.NULL -> {
                        inReader.nextNull()
                        null
                    }
                    JsonToken.NUMBER -> readNumber(inReader)
                    JsonToken.STRING -> inReader.nextString()
                    else -> throw IOException("expecting value: " + inReader.path)
                }
            }

            @Throws(IOException::class)
            private fun readNumber(inReader: JsonReader): Any {
                val doubleValue = inReader.nextDouble()
                if (doubleValue - Math.ceil(doubleValue) == 0.0) {
                    val longValue = doubleValue.toLong()
                    return if (longValue >= Int.MIN_VALUE && longValue <= Int.MAX_VALUE) {
                        longValue.toInt()
                    } else longValue
                }
                return doubleValue
            }

            @Throws(IOException::class)
            private fun readArray(inReader: JsonReader): List<*> {
                val list = ArrayList<Any?>()
                inReader.beginArray()
                while (inReader.peek() != JsonToken.END_ARRAY) {
                    val element = readValue(inReader)
                    list.add(element)
                }
                inReader.endArray()
                return list
            }
        } as TypeAdapter<T>
    }
}